package com.kh.pajx.sop.util.web;

import static com.kh.pajx.sop.util.web.Constants.UTF8;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang.StringEscapeUtils;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encoder;
import org.owasp.esapi.errors.EncodingException;
import org.owasp.esapi.reference.DefaultEncoder;

/**
 * XSS反注入过滤器 <Br/>
 * 未使用Struts2继承HttpServletRequestWrapper，
 * 使用Struts2需继承 StrutsRequestWrapper
 * @author:	azzcsimp
 * @Createdate:	2014年12月13日 上午10:55:03
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

	private String[] filterChars;
	private String[] replaceChars;

	public XssHttpServletRequestWrapper(HttpServletRequest request, String filterChar, String replaceChar,
			String splitChar) {
		super(request);
		/*if (filterChar != null && filterChar.length() > 0) {
			filterChar += splitChar+"<";
			filterChars = filterChar.split(splitChar);
		}
		if (replaceChar != null && replaceChar.length() > 0) {
			replaceChar += splitChar+"＜";
			replaceChars = replaceChar.split(splitChar);
		}
		// 空格 &nbsp;"&" "&amp;"  "'" ,"&#39;" '@ " @ \ @ # @ %@>
		filterChars = new String[]{">","<","#","%","\""};
		replaceChars = new String[]{"&gt;","&lt;","&#35;","&#37;","&quot;"};*/
		filterChars = new String[]{"<",">","#","%","'"};
		replaceChars = new String[]{"＜","＞","＃","％","\'"};
	}

	@Override
	public String getQueryString() {
		String value = super.getQueryString();
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}
	
	/**
	 * 覆盖getParameter方法，将参数名和参数值都做xss过滤<br/>
	 * 如果需要获得原始值，则通过super.getParameterValues(name)来获取<br/>
	 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
	 */
	@Override
	public String getParameter(String name) {
		String value = super.getParameter(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}

	@Override
	public String[] getParameterValues(String name) {
		String[] parameters = super.getParameterValues(name);
		if (parameters == null || parameters.length == 0) {
			return null;
		}
		for (int i = 0; i < parameters.length; i++) {
			parameters[i] = xssEncode(parameters[i]);
		}
		return parameters;
	}
	
	/**
	 * Struts2获取表单属性值，底层使用的是此方法<br/>
	 * 重写此方法做XSS过滤
	 * @see javax.servlet.ServletRequestWrapper#getParameterMap()
	 */
	@Override
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> paramMap = new HashMap<String, String[]>(super.getParameterMap());
		// 对于paraMap为空的直接return
		if (null == paramMap || paramMap.isEmpty()) {
			return paramMap;
		}
		for (Entry<String, String[]> entry : paramMap.entrySet()) {
			String key = entry.getKey();
			String[] values = entry.getValue();
			if (null == values) {
				continue;
			}
			String[] newValues  = new String[values.length];
			for (int i = 0; i < values.length; i++) {
				newValues[i] = xssEncode(values[i]);
			}
			paramMap.put(key, newValues);
		}
		return paramMap;
	}
	
	/**
	 * 覆盖getHeader方法，将参数名和参数值都做xss过滤。<br/>
	 * 如果需要获得原始的值，则通过super.getHeaders(name)来获取<br/> 
	 * getHeaderNames 也可能需要覆盖
	 */
	public String getHeader(String name) {

		String value = super.getHeader(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}
	
	private String xssEncode(String value) {
		if (value == null || value.equals("")) {
			return value;
		}
		Encoder encoder = new DefaultEncoder(Constants.codecList);
		String clean = encoder.canonicalize(value);
		return clean;
	}
	
	
	@Deprecated
	private String xssEncode3(String value) {
		if (value == null || value.equals("")) {
			return value;
		}
		if (value != null) {
			System.out.println("1====>"+value);
			System.out.println("2====>"+ ESAPI.encoder().encodeForHTML(value));
			System.out.println("3====>"+ ESAPI.encoder().canonicalize(value));
			System.out.println("4====>"+ ESAPI.encoder().encodeForHTMLAttribute(value));
			try {
				System.out.println("5====>"+ ESAPI.encoder().encodeForURL(value));
			} catch (EncodingException e) {
				System.out.println("5====>");
			}
			System.out.println("6======>"+StringEscapeUtils.escapeHtml(value));
			System.out.println( ESAPI.encoder().encodeForHTML(ESAPI.encoder().canonicalize(value)));
			System.out.println( ESAPI.encoder().canonicalize(ESAPI.encoder().encodeForHTML(value)));
			System.out.println();
			value = ESAPI.encoder().encodeForHTML(value);
		}
		return value;
	}
	
	@Deprecated
	private String xssEncode1(String value) {
		if (value == null || value.equals("")) {
			return value;
		}

		if (value != null) {
			// NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to avoid encoded attacks.
			//value = ESAPI.encoder().canonicalize(value);

			// Avoid null characters
			value = value.replaceAll("", "");

			// Avoid anything between script tags
			Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid anything in a src='...' type of e­xpression
			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE
					| Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");

			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE
					| Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");

			// Remove any lonesome </script> tag
			scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");

			// Remove any lonesome <script ...> tag
			scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
					| Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid eval(...) e­xpressions
			scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
					| Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid e­xpression(...) e­xpressions
			scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
					| Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid javascript:... e­xpressions
			scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid vbscript:... e­xpressions
			scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");

			// Avoid onload= e­xpressions
			scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
					| Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
		}
		return value;
	}

	/**
	 * 将容易引起XSS漏洞的半角字符直接替换为全角字符
	 * @param value
	 * @return   
	 * @author:	azzcsimp
	 * @Createdate:	2014年12月13日 上午10:15:30
	 */
	@Deprecated
	private String xssEncode2(String s) {
		if (s == null || s.equals("")) {
			return s;
		}
		try {
			s = s.replaceAll("%", "%25");
			s = URLDecoder.decode(s, UTF8);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		for (int i = 0; i < filterChars.length; i++) {
			if (s.contains(filterChars[i])) {
				s = s.replace(filterChars[i], replaceChars[i]);
			}
		}
		return s;
	}
}
